<?php

namespace Application\Auth\Controller;


use Aoe\Attributes\Controller\HttpMust;
use Aoe\Attributes\Controller\SetResponse;
use Aoe\Attributes\Controller\Trace;
use Aoe\Emulator\Controller\Aoe;
use Aoe\Emulator\Controller\Spa;
use Aoe\Emulator\Ifc\Type;
use Aoe\Emulator\Ifc\Vu;
use Aoe\Intent\Request\Channel;
use Aoe\Intent\Request\Http;
use Aoe\Intent\User\User;
use Aoe\Util\Exception;
use Throwable;

/**
 * # 用户登录
 * @noinspection PhpUnused
 */
#[Trace]
#[HttpMust]
#[SetResponse(Spa::class)]
class Admin extends Aoe
{
    /**
     * ## 界面-用户登录
     * @noinspection PhpUnused
     */
    protected function index_action(Http $http): void
    {
        $config = $this->kernel->config;
        $http->response->complete(
            [
                Vu::COMPONENT   => 'login',
                Vu::SCHEMA => [
                    'action' => $http->url->makeUrl('auth/admin/login'),
                    'settings' => [
                        'app'     => $config->get('app'),
                        'title'   => $config->get('title'),
                        'company' => $config->get('company'),
                        'version' => $config->get('version'),
                        'soft'    => $config->get('soft'),
                    ],
                ],
            ],
        );
    }
    
    /**
     * ## 接口-用户退出
     * @noinspection PhpUnused
     */
    protected function logout_action(Http $http): void
    {
        $http->user->logout();
        $http->response->complete($http->url->makeUrl('auth/admin/index'));
    }

    /**
     * ## 接口-用户登录
     * @noinspection PhpUnused
     */
    protected function login_action(Http $http): void
    {
        $this->_launch($http);
        $response = $http->response;
        if (!$response->error) $response->setValue(
            ['command' => ['url' => $http->popCurrentUri()]],
        );
    }
    
    /**
     * ### 内部-执行登录动作
     * @param Http $http
     *
     * @return void
     */
    private function _launch(Http $http): void
    {
        $name     = $http->getParam('user');
        $password = $http->getParam('password');
        
        try {
            if (empty($name) || empty($password)) throw new Exception('用户名/密码不能为空');
            
            $members = $this->create_search()
                ->setElements([Type::ID, 'password'])
                ->orWhere(['user' => $name])
                ->select();
            
            if (empty($members)) throw new Exception('用户名密码错误');
            $member = array_pop($members);
            if (!isset($member['password']) || !password_verify($password, $member['password']))
                throw new Exception('用户名密码错误');

            
            $member[User::KEY_POWERS] = array_reduce($this->_get_roles($member['id']), $this->_get_powers(...), []);
            $http->user->login($member);
            
            $this->_record_admin($http, '登录成功');
            $http->response->ok('登录成功');
            return;
            
        } catch (Throwable $e) {
            $this->_record_admin($http, '登录失败', ['password' => $password]);
            $http->response->error('登录失败：' . $e->getMessage());
        }
    }
    
    /**
     * ### 内部-记录登录信息
     *
     * @param Http   $http
     * @param string $event
     * @param array  $param
     *
     * @return void
     */
    private function _record_admin(Http $http, string $event, array $param = []): void
    {
        
        $user = $http->user;
        
        $param['admin'] = $user->id ?? -1;
        $param['ip']    = $http->ip ?? '127.0.0.1';
        
        $recode = ['event' => $event];
        $recode['admin'] = $user->name ?? '';
        $recode['param'] = json_encode($param);
        $recode['dt'] = time();
        
        try {
            $this->create_entity('record')->insert($recode);
        } catch (Throwable $e) {
            $this->recorder->Error($e->getMessage());
        }
    }
    
    /**
     * ### 内部-根据角色ID获取权限信息
     *
     * @param array $powers
     * @param int $role
     *
     * @return array
     * @throws Throwable
     */
    private function _get_powers(array $powers, int $role): array
    {
        if ($role <= 0) return $powers;
        
        $rows = $this->create_search('power')
            ->orWhere(['role' => $role])
            ->setElements(['resource', 'powers'])
            ->select();
        
        foreach ($rows as $row) $powers[$row['resource']] = $row['powers'] | ($powers[$row['resource']] ?? 0);
        return $powers;
    }
    
    /**
     * ### 内部-根据用户ID获取其所有的角色
     *
     * @param int $id
     *
     * @return array 角色列表
     * @throws Throwable
     */
    private function _get_roles(int $id): array
    {
        if ($id <= 0) return [];
        
        return $this->create_search('role')
            ->orWhere(['member' => $id])
            ->setElements(['id'])
            ->select();
    }
    
    /**
     * ### 内部-注册资源
     * @noinspection PhpUnused
     */
    protected function pass_action(Channel $request): void
    {
        $module      = $request->getParam('module');
        $controllers = $request->getParam('controllers');
        if (!is_array($module) || !is_array($controllers) || !isset($module['name']) || empty($controllers)) return;
        
        $mName = $module['name'];
        $mLabel = $module['label'] ?? $mName;
        
        try {
            $resource = $this->create_entity('resource');
            foreach ($controllers as $controller) {
                if (!isset($controller['name']) || !isset($controller['label'])) continue;
                $resource->insert([
                    'name' => "$mLabel-{$controller['label']}",
                    'path' => strtolower("$mName::{$controller['name']}")
                ]);
            }
        }catch (Throwable) {
            return;
        }
    }
}